/**
  ******************************************************************************
  * @file    WIFI.c
  * @author  tuancheng_Zero
  * @version V0.1
  * @date    08/05/2024
  * @brief   WIFI function Handle 
  ******************************************************************************
*/ 
#include <includes.h>
#include "WIFI.h"
#include "WIFI_Driver.h"
#include "app_network.h"

//函数声明
void WIFI_Init(void);
static int GPRS_DRIVER_Send(const char *pBuff, int len);
static int send_transient(const char *pATSend, int len);
static int send_condition(const char *pATSend, int len);
static int WIFI_CloseConnect(const char *pATSend, int len);
static int Send_Send_Cmd(const char *pATSend, int len);
static int Send_Send_Data(const char *pATSend, int len);
static int WIFI_ConnectWifi(const char *pATSend, int len);
static int WIFI_ConnectServer(const char *pATSend, int len);
static int WIFI_ClearFlag(const char *pBuff, int len);
static int WIFI_ModuleReset(const char *pBuff, int len);
static int WIFI_WaitReady(const char *pBuff, int len);
static int funOK(char *pATSend, int len);
static int funNULL(char *pATSend, int len);
static int Send_OK(char *pATSend, int len);
static int change_ipr(char *pBuff, int len);
static int WIFI_ConnectInfo(char *pATSend, int len);
static int WIFI_TCPConnetInfo(char *pATSend, int len);

// 外部引用变量
extern uint8_t TxBuffer[SendBUFFMAX];               // DMA中的串口发送缓存
extern uint8_t TemArrRxBuffer[2][DMARxBuffer];      // 存储接收
// 局部变量
static uint16_t sSend_LEN;                          // DMA的串口缓存中待发送数据长度
static uint8_t fristFlag;                           // 第一次发送标志
static const SGPRS_AT *s_GPRS_AT;                   // 指向状态机结构体数组的首地址
static uint32_t s_uiOneTimeOutIdx;                  // 多次发送之间的间隔时间（1ms为单位），同时也是发送AT指令后的等待通讯模块回应的最长时间
static uint32_t s_uiMaxTimeOutIdx;                  // 多次发送时的次数计数器
static uint8_t wifi_ClosConnTimes;                  // 关闭连接的次数计数器
static uint32_t iSendLen;                           // 发送缓存中的待发送数据的长度，以字符为单位
static char SendBuff[SendBUFFMAX];                  // 待发送到云平台的数据的中间缓存，包括心跳包、瞬态包、告警包....
// 全局变量
uint8_t ucStatus;                                   // 状态机的当前状态序号，也就是状态机结构体数组的下标
uint8_t already_send_transient;                     // 已经发送瞬态包（快速上线）
const SGPRS_AT *pGPRS_AT;                           // 当前状态机
char ucATSendBuff[ATSENDBUFFMAX];                   // 发送AT指令的缓存
struct HeartBeatReport heartBeatReport_st;          // 心跳包计数器

//工作流程表
static const SGPRS_AT sGPRS_AT[] = // 以下涉及到时间单位都是1ms
{
    /* AT指令，发送函数，发送后的等待时间或间隔，发送次数限制，回应的字符串标准，接收函数，下一模式，超时模式 */
    {"", WIFI_ModuleReset, 2000, 1, "ready", funOK, sWIFI_AT_Init, sWIFI_AT_Init},                                           // 0 WIFI模块复位
    {"", WIFI_ClearFlag, 2000, 1, "", funNULL, sWIFI_AT_powerWait, sWIFI_AT_powerWait},                                      // 1 复位后初始化标志
    {"", WIFI_WaitReady, 3000, 1, "ready", funOK, sWIFI_AT_ATE0, sWIFI_AT_ATE0},                                             // 2 等待WIFI模块就绪
    {"ATE0\r\n", GPRS_DRIVER_Send, 2000, 2, "OK", funOK, sWIFI_AT_WifiMode, sWIFI_AT_RST},                                   // 3 关闭回显
    {"AT+CWMODE=1\r\n", GPRS_DRIVER_Send, 500, 3, "OK", funOK, sWIFI_AT_change_ipr, sWIFI_AT_RST},                           // 4 将WIFI模块设为Station模式
    {"AT+UART_CUR=256000,8,1,0,1\r\n", GPRS_DRIVER_Send, 500, 4, "OK", change_ipr, sWIFI_AT_WifiCheck, sWIFI_AT_RST},        // 5 更改波特率
    {"AT+CWJAP?\r\n", GPRS_DRIVER_Send, 2000, 3, "+CWJAP:", WIFI_ConnectInfo, sWIFI_AT_WifiState, sWIFI_AT_WifiAssign},      // 6 判断是否连接WIFI
    {"AT+CWQAP\r\n", GPRS_DRIVER_Send, 2000, 3, "OK", funOK, sWIFI_AT_WifiAssign, sWIFI_AT_WifiAssign},                      // 7 关闭当前wifi连接
    {"AT+CWJAP=", WIFI_ConnectWifi, 20000, 0, "WIFI GOT IP", funOK, sWIFI_AT_WifiState, sWIFI_AT_WifiAssign},                // 8 尝试连接指定WiFi
    {"AT+CIPSTATUS\r\n", GPRS_DRIVER_Send, 500, 2, "STATUS:", WIFI_TCPConnetInfo, sWIFI_AT_WifiCSQ, sWIFI_AT_WifiCheck},     // 9 查询TCP/IP状态(1:未连接wifi 2:获取了IP地址 3:建立TCP连接)
    {"AT+CWJAP?\r\n", GPRS_DRIVER_Send, 500, 1, "+CWJAP:", WIFI_ConnectInfo, sWIFI_AT_OpenSocket, sWIFI_AT_OpenSocket},      // 10 查询信号强度
    {"AT+CIPSTART=\"TCP\",", WIFI_ConnectServer, 1000, 5, "CONNECT", funOK, sWIFI_AT_SendTransient, sWIFI_AT_CloseSocket},   // 11 开始Socket服务
    {"AT+CIPCLOSE\r\n", WIFI_CloseConnect, 500, 2, "OK", funOK, sWIFI_AT_OpenSocket, sWIFI_AT_RST},                          // 12 关闭连接
    {"", send_transient, 1, 1, "", funOK, sWIFI_AT_CheckSend, sWIFI_AT_CheckSend},                                           // 13 发送瞬态数据（快速上线）
    {"", send_condition, 1, 0, "ERROR", funOK, sWIFI_AT_CloseSocket, sWIFI_AT_CheckSend},                                    // 14 检查是否需要发送数据到云平台
    {"AT+CIPSEND=", Send_Send_Cmd, 200, 3, ">", funOK, sWIFI_AT_SendData, sWIFI_AT_CloseSocket},                             // 15 发送AT命令
    {"", Send_Send_Data, 2000, 3, "SEND OK", Send_OK, sWIFI_AT_CheckSend, sWIFI_AT_CloseSocket},                             // 16 发送数据命令
    {"AT+CWJAP?\r\n", GPRS_DRIVER_Send, 500, 2, "+CWJAP:", WIFI_ConnectInfo, sWIFI_AT_CheckSend, sWIFI_AT_WifiState},        // 17 经常查询信号强度(暂时不用)
};

// WIFI 模块初始化
void WIFI_Init(void){
    GPRS_DRIVER_SetATs(115200);
    s_GPRS_AT = &sGPRS_AT[0];
    pGPRS_AT = s_GPRS_AT + sWIFI_AT_powerWait;
    // 初始化标志
    fristFlag = 1;
    sSend_LEN = 0;
    iSendLen = 0;
    ucStatus = 0;
    wifi_ClosConnTimes = 0;
    heartBeatReport_st.sendTimer = 0;
    heartBeatReport_st.totalTimer = 0;
    // 复位WIFI模块
    WIFI_RST_HIGH();
    WIFI_RST_LOW();
}

/**
 * @brief 心跳包处理函数
 * @brief 发送数据后，每 5 秒 发送一次确认，如果 24 次后仍未确认，可能 TCP 连接异常，需要关闭当前连接，转到打开连接重新建立连接。
 * @brief TCP心跳：定时发送小数据包，来检测 TCP 连接是否正常。
 * @brief 100ms进入一次
 */
void WIFI_HeartReportHandle(void){
    //组织发送心跳包
    if (heartBeatReport_st.sendTimer >= WIFI_HeartBeat_Interval && ucStatus == sWIFI_AT_CheckSend && iSendLen == 0){
        NetWork_HeartReport(NetWork_UpdateWifiSyncID()); // 组织无特殊功能的普通心跳报文
        /* 开始计时 */
        heartBeatReport_st.sendTimer = 0;
    }
    //如果发送心跳包后20秒没有收到响应，断开TCP进行重新连接
    if (heartBeatReport_st.totalTimer > WIFI_HeartBeat_ScanTime * 20 && ucStatus >= sWIFI_AT_CheckSend && iSendLen == 0){
        WIFI_SetStatus(sWIFI_AT_CloseSocket);
    }
    // 计时累加
    heartBeatReport_st.sendTimer++;
    heartBeatReport_st.totalTimer++;
}

/// @brief WIFI工作流程处理函数
/// @param t 时间间隔
void WIFI_ProcessHandle(int t){ // 通讯流程的状态机函数
    if (s_GPRS_AT == NULL)
        return;
    u16 len;

    if (fristFlag){
        // 必须放前面否则函数里面置1无效
        fristFlag = 0;
        pGPRS_AT->sendFunction(pGPRS_AT->pATSend, strlen(pGPRS_AT->pATSend));
    }

    // 发送超时
    s_uiOneTimeOutIdx += t;
    if (s_uiOneTimeOutIdx > pGPRS_AT->uiOneTimeOut){ // 检查2次发送之间的间隔时间是否到，同时也是发送AT指令后的等待通讯模块回应的时间
        s_uiOneTimeOutIdx = 0;
        s_uiMaxTimeOutIdx++; // 如果存在多次发送需求则累计次数，并检查次数是否超过需求
        if (pGPRS_AT->uiMaxTimeOut > 0 && s_uiMaxTimeOutIdx >= pGPRS_AT->uiMaxTimeOut){
            WIFI_SetStatus(pGPRS_AT->ucTimeOutStatus); // 如果次数超过需求时，则设置下一个状态为超时所指向的状态
        }
        else{
            len = strlen(pGPRS_AT->pATSend);                // 如果不超时则计算AT发送指令字符串的长度
            pGPRS_AT->sendFunction(pGPRS_AT->pATSend, len); // 并发送AT指令到通讯模块
        }
    }
}

// WIFI模块复位
static int WIFI_ModuleReset(const char *pBuff, int len){
    WIFI_RST_HIGH();
    WIFI_RST_LOW();
    return 1;
}

// WIFI清除标志
static int WIFI_ClearFlag(const char *pBuff, int len){
    // 重新设定波特率
    GPRS_DRIVER_SetATs(115200);
    // 清除标志参数
    iSendLen = 0;
    ucStatus = 0;
    sSend_LEN = 0;
    wifi_ClosConnTimes = 0;
    heartBeatReport_st.sendTimer = 0;
    heartBeatReport_st.totalTimer = 0;
    return 1;
}

// WIFI等待模块就绪
static int WIFI_WaitReady(const char *pBuff, int len){
    return 1;
}

// 更改与网络模块的通讯波特率
static int change_ipr(char *pBuff, int len){
    GPRS_DRIVER_SetATs(256000);
    return 1;
}

// TCP连接状态判断
static int WIFI_TCPConnetInfo(char *pATSend, int len){
    uint8_t status = pATSend[0];
    
    status = atoi((char *)&status);
    if(status == 0){ // 未初始化状态
        WIFI_SetStatus(sWIFI_AT_RST);
    }
    else if(status == 1){ // 已初始化但未连接wifi
        WIFI_SetStatus(sWIFI_AT_WifiCheck);
    }
    else if(status == 2){ // 已连接wifi,并获取IP地址
        WIFI_SetStatus(sWIFI_AT_WifiCSQ);
    }
    else if(status == 3){ // 已建立TCP,UDP连接
        WIFI_SetStatus(sWIFI_AT_CheckSend);
    }
    else if(status == 4){ // 所有TCP,UDP连接已断开
        WIFI_SetStatus(sWIFI_AT_WifiCSQ);
    }
    else if(status == 5){ // 连接过wifi,但未连接上
        WIFI_SetStatus(sWIFI_AT_WifiCheck);
    }
    return 1;
}

// 查询WIFI连接状态
static int WIFI_ConnectInfo(char *pATSend, int len){
    char ssid[50] = {0}; // WIFI名称
    char mac[50] = {0};  // MAC地址
    int32_t channel = 0; // 通道号
    int32_t csq = 0;     // WIFI信号强度
    int32_t pci_en = 0;  // PCI认证

    // 使用 sscanf 提取字段
    sscanf(pATSend, "\"%[^\"]\",\"%[^\"]\",%d,%d,%d", ssid, mac, &channel, &csq, &pci_en);

    // 如果当前连接的WIFI与配置的WIFI不同则连接配置wifi
    if(WIFI_GetStatus() == sWIFI_AT_WifiCheck){
        uint8_t wifiName[32] = {0};

        ReadDataBaseDataContent(Addr_BaseCfg_WifiName, (uint16_t *)wifiName, 16);    //获取配置的WIFI名称
        if(strcmp((char *)wifiName, (char *)ssid) != 0){
            WIFI_SetStatus(sWIFI_AT_DisconnectWifi);
            return 0;
        }
    }
    return 1;
}

// 连接到指定wifi
static int WIFI_ConnectWifi(const char *pATSend, int len){
#if 1
    // 数据组织
    sprintf(ucATSendBuff, "%s\"%s\",\"%s\"\r\n", pATSend, share_parameterCfgData_st.baseData_st.WifiName, share_parameterCfgData_st.baseData_st.WifiPassWord);
#else
    sprintf(ucATSendBuff, "%s\"%s\",\"%s\"\r\n", pATSend, "TP-LINK_9727", "tuancheng");
#endif
    // 获取发送长度
    len = strlen(ucATSendBuff);
    GPRS_DRIVER_Send(ucATSendBuff, len);
    return 0;
}

// TCP连接操作函数
static int WIFI_ConnectServer(const char *pATSend, int len){
#if 1
    sprintf(ucATSendBuff, "%s\"%s\",%d\r\n", pATSend, share_parameterCfgData_st.baseData_st.ServerHostName, share_parameterCfgData_st.baseData_st.ServerPort);
#else
    // sprintf(ucATSendBuff, "%s\"%s\",%d\r\n", pATSend, "899y97i456.zicp.fun", 25814);
    // sprintf(ucATSendBuff, "%s\"%s\",%d\r\n", pATSend, "c6c4b66854713320.natapp.cc", 19995);
    sprintf(ucATSendBuff, "%s\"%s\",%d\r\n", pATSend, "tcp.i6yun.com", 15000);
#endif

    len = strlen(ucATSendBuff);
    GPRS_DRIVER_Send(ucATSendBuff, len);

    return 0;
}

// 关闭TCP连接
static int WIFI_CloseConnect(const char *pATSend, int len){
    /* 心跳包重新计数 */
    heartBeatReport_st.sendTimer = 0;
    heartBeatReport_st.totalTimer = 0;

    iSendLen = 0;
    if (wifi_ClosConnTimes >= 5)
    { // 如果进入该函数5次则复位wifi模块
        WIFI_SetStatus(sWIFI_AT_RST);
        wifi_ClosConnTimes = 0;
        return 0;
    }
    GPRS_DRIVER_Send(pATSend, len);
    wifi_ClosConnTimes++;

    return 0;
}

// 发送瞬态包
static int send_transient(const char *pATSend, int len){
    if (already_send_transient){
        // 等待更新时间
        OSTimeDlyHMSM(0, 0, 0, 500);
        // 2.发送瞬态包
        Network_RealTimeData(NetWork_UpdateWifiSyncID());
        already_send_transient = 0;
    }
    else{
        // 1. 先发送心跳包更新时间
        NetWork_HeartReport(NetWork_UpdateWifiSyncID());
        already_send_transient = 1;
    }
    return 0;
}

// 检查是否有数据要发送
static int send_condition(const char *pATSend, int len){
    // 检查发送缓冲中是否有数据需要发送，如有则进入发送数据的指令流程
    if (iSendLen != 0){
        WIFI_SetStatus(sWIFI_AT_SendDataBefore);
    }
    return 0;
}

// 数据发送完成
static int Send_OK(char *pATSend, int len){
    iSendLen = 0;
    wifi_ClosConnTimes = 0;

    /* 发送瞬态包（快速上线） */
    if (already_send_transient){
        WIFI_SetStatus(sWIFI_AT_SendTransient);
        return 0;
    }

    return 1;
}

// 发送数据命令
static int Send_Send_Cmd(const char *pATSend, int len){
    if (iSendLen == 0)
        return -1; // 检查之前发送的数据是否已经完成
    if (len >= ATSENDBUFFMAX)
        return 0; // 检查待发送的指令长度是否超过最大限度

    strcpy(ucATSendBuff, pATSend);
    sprintf(&ucATSendBuff[len], "%d\r\n", iSendLen); // 在发送AT指令缓冲的末尾加上待发送数据的长度，组装成一个完整的AT指令
    len = strlen(ucATSendBuff);

    GPRS_DRIVER_Send(ucATSendBuff, len); // 通过串口的DMA缓存将指令发送到通讯模块

    return 0;
}

// 发送数据内容 (要先执行发送数据命令函数)
static int Send_Send_Data(const char *pATSend, int len){
    if (iSendLen == 0)
        return -1;
    GPRS_DRIVER_Send(SendBuff, iSendLen);

    return 0;
}

// 设置新的流程状态
void WIFI_SetStatus(u8 ucstatus){
    if (s_GPRS_AT == NULL)
        return;

    pGPRS_AT = s_GPRS_AT + ucstatus; // 获取新状态在结构体数组指针的下标
    ucStatus = ucstatus;
    s_uiOneTimeOutIdx = 0; // 单次超时的时间
    s_uiMaxTimeOutIdx = 0; // 初始化超时次数计数器
    fristFlag = 1;         // 改变状态设置1
}

// 装载待发送数据到 DMA的串口缓存中，并启动DMA开始发送数据到通讯模块，如果遇到之前的数据还没发送完则返回-1，如果数据长度大于缓存则返回-2
static int GPRS_DRIVER_Send(const char *pBuff, int len){
    if (len == 0)
        return 0;
    if (sSend_LEN != 0)
        return -1; // 在DMA发送缓冲中还有之前未发送完的数据待发送，所以返回-1
    if (len >= sizeof(TxBuffer))
        return -2; // 待发送的数据长度大于DMA发送缓存长度，返回-2

    memcpy((char *)&TxBuffer[0], (char *)pBuff, len);  // 装载待发送数据到 DMA的串口缓存中，并启动DMA开始发送数据到通讯模块
    USARTy_Tx_DMA_Channel->MADDR = (uint32_t)TxBuffer; // 内存地址
    USARTy_Tx_DMA_Channel->TXNUM = len;                // 发送数据长度
    sSend_LEN = len;

    DMA_EnableChannel(USARTy_Tx_DMA_Channel, ENABLE); // 启动dma 发送

    return sSend_LEN;
}

// 获取/修改发送数据长度
uint32_t * WIFI_GetSendLen(void){
    return &iSendLen;
}

// 获取/修改发送缓冲
char * WIFI_GetSendBuff(void){
    return &SendBuff[0];
}

// 空函数
static int funNULL(char *pATSend, int len){
    return 0;
}
// 空函数
static int funOK(char *pATSend, int len){
    return 1;
}

// 获取当前流程状态
uint8_t WIFI_GetStatus(void){
    return ucStatus;
}

// dma接收完成中断服务函数
void USARTy_Rx_DMA_Function(void){
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
    OSIntEnter();

    DMA_EnableChannel(USARTy_Rx_DMA_Channel, DISABLE);          // 禁用DMA
    DMA_ClearFlag(USARTy_Rx_DMA_FLAG, DMA1);                    // 清标志

    USARTy_Rx_DMA_Channel->TXNUM = sizeof(TemArrRxBuffer[0]);   // 从新设定缓冲大小
    DMA_EnableChannel(USARTy_Rx_DMA_Channel, ENABLE);           // 处理完,重开DMA

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
    OS_EXIT_CRITICAL();
}

// dma发送完成中断服务函数
void USARTy_Tx_DMA_Function(void){
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
    OSIntEnter();

    DMA_EnableChannel(USARTy_Tx_DMA_Channel, DISABLE); // 关闭DMA
    DMA_ClearFlag(USARTy_Tx_DMA_FLAG, DMA1);           // 清标志
    sSend_LEN = 0;

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
    OS_EXIT_CRITICAL();
}
